package studio.raptor.databus.graph.service;

import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import studio.raptor.databus.graph.common.DirectGraph;
import studio.raptor.databus.graph.domain.Event;
import studio.raptor.databus.graph.domain.Events;
import studio.raptor.databus.graph.repository.EventRepository;
import studio.raptor.databus.graph.rule.EventDirectGraph;
import studio.raptor.databus.graph.rule.EventRelationship;


/**
 * @author jack
 * @since 1.0
 */
@Service
public class EventService {

  private static final Logger log = LoggerFactory.getLogger(EventService.class);

  @Autowired
  EventRepository eventRepository;

  @Autowired
  EventRelationship eventRelationship;

  @Autowired
  EventDirectGraph eventDirectGraph;


  /**
   * 删除事件
   *
   * @param events 需要被删除的事件集合
   */
  @Transactional(timeout = 10)
  public void deleteEvents(Events events) {
    List<Event> eventList = events.getEvents();
    for (int i = 0, size = eventList.size(); i < size; i++) {
      Event event = eventList.get(i);
      deleteEventNode(event.getCode(), event.getEventId(), event.getName());
    }
  }

  @Transactional(timeout = 10)
  public void deleteEventNode(String code, String eventId, String label) {
    DirectGraph graph = eventDirectGraph.getDirectGraph(code);
    //删除内存图中的节点
    Set<String> childNodes = graph.deleteNode(eventId);
    //删除内存的依赖关系
    eventRelationship.updateRelationship(eventId, code);
    //删除数据库的节点
    eventRepository.deleteNodeByEventId(eventId, code, label);
    //重新节点建立关系
    if (null != childNodes && !childNodes.isEmpty()) {
      eventRepository.createRelationWithRoot(childNodes, code, label);
    }
  }

  /**
   * 根据接口编码获取一定数量的可以被发送的事件
   *
   * @param code 接口编码
   * @param status 事件状态
   * @param limit 获取节点数量最大值
   */
  public Set<String> fetchEvent(String code, String status, int limit) {
    DirectGraph graph = eventDirectGraph.getDirectGraph(code);
    Set<String> result = graph.fetchNodes(limit, status);
    return result;
  }

  /**
   * 保存事件到图数据库，从内存中获取关联的事件，在图数据库中创建事件间的关联关系
   * 保存事件和创建节点间的关联关系设置在同一个事务中，防止事件保存成功后，创建关联关系失败，导致后续获取事件时顺序有问题
   * 事务超时时间设置为3s
   */
  @Transactional(timeout = 3000)
  public void addEvents(Events events) {
    List<Event> eventList = events.getEvents();
    for (int i = 0, size = eventList.size(); i < size; i++) {
      addEvent(eventList.get(i));
    }
  }

  @Transactional(timeout = 30)
  public void addEvent(Event event) {

    String code = event.getCode();
    String eventId = event.getEventId();
    log.info("consumer:{},add event:{}", code, eventId);

    DirectGraph graph = eventDirectGraph.getDirectGraph(code);

    if (graph.containEvent(eventId)) {
      log.debug("the code:{},node [{}] exists in the graph ", code, eventId);
      return;
    }
    if (log.isDebugEnabled()) {
      log.debug("the event direct graph:{}", graph.getEventGraph().toString());
    }

    //1 从内存中获取事件的依赖关系
    Set<String> depends = eventRelationship.getDependEventId(event);
    if (log.isDebugEnabled()) {
      log.debug("the event:{},depend->{}", event.getEventId(), depends.toString());
    }

    //1.没有依赖，直接创建节点并与root节点建立关联
    if (depends.isEmpty()) {
      graph.addNode(eventId, event.getStatus());
      eventRepository.createRelationshipWithRoot(event);
      return;
    }
    //2 如果存在依赖事件节点保存到图数据库中
    eventRepository.insertEvent(event);
    //3 事件节点保存到内存的有向无环图，并建立节点间的关联关系，过滤已经建立过关系的图
    depends = graph.putEdges(eventId, event.getStatus(), depends);
    if (!depends.isEmpty()) {
      eventRepository.insertAndCreateRelation(eventId, depends, event.getName());
    }
  }


  /**
   * 批量更新图数据库中事件节点的状态
   *
   * @param events 需要被更新的事件
   */
  @Transactional(timeout = 30)
  public void updateEvents(Events events) {
    List<Event> eventList = events.getEvents();

    for (int i = 0, size = eventList.size(); i < size; i++) {
      Event event = eventList.get(i);
      DirectGraph graph = eventDirectGraph.getDirectGraph(event.getCode());
      //更新内存中节点的状态
      graph.updateEventNodeStatus(event.getEventId(), event.getStatus());
      updateEventStatus(event.getEventId(), event.getName(), event.getCode(), event.getStatus());
    }
  }

  @Transactional(timeout = 30)
  public void updateEventStatus(String eventId, String label, String code, String status) {
    eventRepository.updateEventStatusByEventId(eventId, label, code, status);
  }


}
